home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / MPS disk 1.0.1 / Chapter 09 / DEF's / Neat Stuff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-19  |  22.3 KB  |  658 lines  |  [TEXT/KAHL]

  1. #include "Neat Stuff.h"
  2. #include "GestaltEqu.h"
  3. #include "Palettes.h"            // For GetGray call.
  4.  
  5. /*******************************************************************************
  6.  
  7.     Global variables, type definitions, and function prototypes.
  8.  
  9. *******************************************************************************/
  10.  
  11. const RGBColor    kBlack    = {0x0000, 0x0000, 0x0000};
  12. const RGBColor    kWhite    = {0xFFFF, 0xFFFF, 0xFFFF};
  13. const RGBColor    kGray    = {0x8000, 0x8000, 0x8000};
  14.  
  15. ListHandle        gMyList;
  16. Rect            gNextWindowRect    = {40, 10, 180, 110};
  17. const Point        gWindowStagger    = {20, 20};
  18. const Point        gWindowStart    = {40, 10};
  19.  
  20.  
  21. // Function prototypes. Some routines are declared as using the Pascal
  22. // calling convention because they are called by the Toolbox.
  23.  
  24. pascal    Boolean    ListFilter(DialogPtr dlg, EventRecord* event, short* itemHit);
  25.         void    FillList(ListHandle theList);
  26.         void    FlashDialogItem(DialogPtr dlg, short itemToFlash);
  27. pascal    void    MyLDEF(    short lMessage, Boolean lSelect, Rect* lRect,
  28.                         Cell lCell, short lDataOffset, short lDataLen,
  29.                         ListHandle lHandle);
  30.         void     DrawItem(Boolean selected, Rect* bounds, ListItemHandle data,
  31.                          ListHandle lHandle);
  32.         void    DrawNewWay(Boolean selected, Rect* bounds, ListItemHandle data,
  33.                             ListHandle lHandle);
  34.         void    DrawOldWay(Boolean selected, Rect* bounds, ListItemHandle data,
  35.                             ListHandle lHandle);
  36.         void    DrawTheIcon(Rect *bounds, ListHandle lHandle, ListItemHandle data);
  37.         void    DrawTheText(Rect *bounds, ListHandle lHandle, ListItemHandle data);
  38.         void    GrayOut(Rect* bounds);
  39.         void     DrawSmallIcon(short id, short index, Rect* where);
  40.  
  41.  
  42. /*******************************************************************************
  43.  
  44.     DoShowCustomList
  45.  
  46.     Brings up a modal dialog containing a list and an OK button. The list is
  47.     created with a custom LDEF that shows items with an icon drawn to their
  48.     left.
  49.  
  50.     After we create the dialog with a call to GetNewDialog, we prepare our
  51.     list. This list is implemented as a userItem. First, we get the bounds of
  52.     the userItem. We treat the bounds as a maximum size, and shrink it as
  53.     necessary so that an integral number of lines will appear in the list. The
  54.     size of each line is determined by the height of the font the dialog uses.
  55.     However, since we’ll be drawing icons along with each text item, and the
  56.     icons are 16 pixels high, we make sure that each line is at least 16
  57.     pixels high, too.
  58.  
  59.     Once we’ve determined and set the height for the userItem, we call the
  60.     List Manager to create the list we draw inside of it. Because we’re using
  61.     our stub LDEF that can’t handle the LInitMsg, we must call the list
  62.     definition code ourselves so that it can perform any initialization. After
  63.     that, we set the list flags so that only one item is selected at a time.
  64.     Then we fill up the list with some items, and finally turn on the flag
  65.     that allows the list of draw.
  66.  
  67.     All that needs to be done now is call ModalDialog. After the user clicks
  68.     OK, we dispose of the list and the dialog and leave. A real program would
  69.     probably so something with the item that the user selected, but we’ll
  70.     leave that up to you.
  71.  
  72. *******************************************************************************/
  73. void DoShowCustomList()
  74. {
  75.     DialogPtr    dlg;
  76.     short        itemHit;
  77.     short        iKind;
  78.     Handle        iHandle;
  79.     Rect        iRect;
  80.     FontInfo    info;
  81.     Point        cSize;
  82.     Rect        dataBounds = {0, 0, 0, 1};
  83.  
  84.     dlg = GetNewDialog(130, nil, (WindowPtr) -1);
  85.     SetPort(dlg);
  86.  
  87.     GetDItem(dlg, 2, &iKind, &iHandle, &iRect);
  88.  
  89.     GetFontInfo(&info);
  90.     cSize.h = iRect.right - iRect.left - 15;
  91.     cSize.v = info.ascent + info.descent + info.leading;
  92.     if (cSize.v < 16)
  93.         cSize.v = 16;
  94.  
  95.     iRect.bottom = iRect.top + ((iRect.bottom - iRect.top) / cSize.v) * cSize.v;
  96.     SetDItem(dlg, 2, iKind, iHandle, &iRect);
  97.     iRect.right -= 15;                        // To allow for the list’s scroll bar.
  98.  
  99.     gMyList = LNew(&iRect, &dataBounds, cSize, kGenericLDEF, dlg,
  100.                     FALSE,                    // delay drawing list
  101.                     FALSE,                    // no grow box
  102.                     FALSE,                    // no horizontal scroll bar
  103.                     TRUE);                    // has vertical scroll bar
  104.  
  105.     MyLDEF(lInitMsg,
  106.             FALSE, nil, cSize, 0, 0,        // these are all dummy values
  107.             gMyList);
  108.  
  109.     (**gMyList).selFlags = lOnlyOne;        // “There can be only one!”
  110.  
  111.     FillList(gMyList);
  112.     LDoDraw(TRUE, gMyList);
  113.  
  114.     do {
  115.         ModalDialog(ListFilter, &itemHit);
  116.     } while (itemHit != ok);
  117.  
  118.     LDispose(gMyList);
  119.     DisposDialog(dlg);
  120. }
  121.  
  122.  
  123. /*******************************************************************************
  124.  
  125.     DoShowCustomWindow
  126.  
  127.     Simply show a window. This window uses the custom Windoid WDEF that we’ve
  128.     written. Don’t be fooled, though! This window may _look_ like a floating
  129.     window, but it doesn’t act like one. It takes more than a custom WDEF to
  130.     make a window float.
  131.  
  132. *******************************************************************************/
  133. void DoShowCustomWindow()
  134. {
  135.     Ptr            p            = nil;
  136.     Str255        title        = "\pNeat Window";    // Title not shown in this WDEF
  137.     Boolean        visible        = TRUE;
  138.     short        procID        = (1000 * 16);        // WDEF #1000
  139.     WindowPtr    behind        = (WindowPtr) -1;
  140.     Boolean        goAwayFlag    = TRUE;
  141.     long        refCon        = 0;
  142.     WindowPtr    myWindow;
  143.  
  144.     myWindow = NewCWindow(p, &gNextWindowRect, title, visible, procID, behind,
  145.                             goAwayFlag, refCon);
  146.  
  147.     //
  148.     // We stagger our windows. Update gNextWindowRect so that the next
  149.     // window we create is moved down and to the right of the one we
  150.     // just created.
  151.     //
  152.  
  153.     OffsetRect(&gNextWindowRect, gWindowStagger.h, gWindowStagger.v);
  154.     if (gNextWindowRect.bottom > qd.screenBits.bounds.bottom)
  155.         OffsetRect(&gNextWindowRect, 0, gWindowStart.v - gNextWindowRect.top);
  156.     if (gNextWindowRect.right > qd.screenBits.bounds.right)
  157.         OffsetRect(&gNextWindowRect, gWindowStart.h - gNextWindowRect.left, 0);
  158. }
  159.  
  160.  
  161. /*******************************************************************************
  162.  
  163.     ListFilter
  164.  
  165.     This is the ModalDialog filter we use for the dialog that contains our
  166.     list. It handles updates and mouse clicks for our list, as well as some
  167.     special handling of the OK button.
  168.  
  169.     When the user clicks the mouse in our dialog, we need to see if they
  170.     clicked on our list. This is done by getting the bounds of the list and
  171.     using it and the mouseDown location in a call to PtInRect. If PtInRect
  172.     returns TRUE, the user clicked in our list, and we need to call LClick to
  173.     handle it. LClick is a List Manager routine that takes care of clicks on
  174.     the list items or in the scrollbar(s) attached to the list. If LClick
  175.     returns TRUE, it’s reporting that this click was the second part of a
  176.     double-click. If it was, and if the item wasn’t disabled, we simulate a
  177.     click on the OK button.
  178.  
  179.     If the user pressed a key, we check to see if that key was either the
  180.     Return or Enter key. If so, we simulate a click on the OK button. This is
  181.     standard action for dialogs, and is normally handled by the Dialog
  182.     Manager. However, if an application implements a custom dialog filter, it
  183.     is up to that application to re-implement that standard behavior.
  184.     Fortunately, it only takes a few lines, as you can see.
  185.  
  186.     Finally, if there is an update event for the dialog, we need to do two
  187.     things. First, we put a bold outline around the OK button, signifying that
  188.     this is the button that will be selected if the user presses Return or
  189.     Enter. Second, we call LUpdate to draw our list.
  190.  
  191. *******************************************************************************/
  192. pascal Boolean ListFilter(DialogPtr dlg, EventRecord* event, short* itemHit)
  193. {
  194.     short        iKind;
  195.     Handle        iHandle;
  196.     Rect        iRect;
  197.     short        radius;
  198.     char        key;
  199.     Point        localMouse;
  200.  
  201.     switch (event->what) {
  202.         case mouseDown:
  203.             localMouse = event->where;
  204.             GlobalToLocal(&localMouse);
  205.             GetDItem(dlg, 2, &iKind, &iHandle, &iRect);
  206.             if (PtInRect(localMouse, &iRect)) {
  207.                 if (LClick(localMouse, event->modifiers, gMyList)) {
  208.                     *itemHit = ok;
  209.                     FlashDialogItem(dlg, *itemHit);
  210.                 }
  211.                 return TRUE;
  212.             }
  213.             return FALSE;
  214.  
  215.         case keyDown:
  216.             key = event->message & charCodeMask;
  217.             if ((key == kReturn) || (key == kEnter)) {
  218.                 *itemHit = ok;
  219.                 FlashDialogItem(dlg, *itemHit);
  220.                 return TRUE;
  221.             }
  222.             return FALSE;
  223.  
  224.         case updateEvt:
  225.             SetPort(dlg);
  226.             GetDItem(dlg, ok, &iKind, &iHandle, &iRect);
  227.             InsetRect(&iRect, -4, -4);
  228.             radius = (iRect.bottom - iRect.top) / 2;
  229.             if (radius < 16)
  230.                 radius = 16;
  231.             PenNormal();
  232.             PenSize(3,3);
  233.             FrameRoundRect(&iRect, radius, radius);
  234.  
  235.             GetDItem(dlg, 2, &iKind, &iHandle, &iRect);
  236.             InsetRect(&iRect, -1, -1);
  237.             PenNormal();
  238.             FrameRect(&iRect);
  239.  
  240.             LUpdate(dlg->visRgn, gMyList);
  241.  
  242.             return FALSE;
  243.  
  244.         default:
  245.             return FALSE;
  246.     }
  247. }
  248.  
  249.  
  250. /*******************************************************************************
  251.  
  252.     FillList
  253.  
  254.     Fills up our list with some data. We’ve defined a structure that contains
  255.     data for a single item in the list. It looks like this:
  256.  
  257.             StringHandle    theString;
  258.             short            iconID;
  259.             short            iconIndex;
  260.             Boolean            enabled;
  261.  
  262.     This structure is used to contain the text to be drawn, the icon to draw
  263.     with it, and a flag indicating if the item is enabled (drawn in black) or
  264.     disabled (drawn in gray).
  265.  
  266.     To squeeze the most out of memory that we can, we don’t actually store
  267.     this structure in our ListRec, even though we could if we wanted. The
  268.     reason why we don’t is because we can only store 32K of data in our
  269.     ListRec. If we stored the entire structure above (which takes about 10
  270.     bytes), we would only be able to insert 3,276 items into the list.
  271.     Instead, we create our record dynamically with NewHandle, and store the
  272.     handle (which is only 4 bytes long) into the list. This allows us to
  273.     insert up to 8,192 items.
  274.  
  275.     The data for our list comes from the root directory of whatever volume
  276.     we’re running on. We make repeated calls to PBGetCatInfo to get
  277.     information on all the files at the root level. For each item, we record
  278.     its name and determine what icon should be used to represent it. To
  279.     simulate Standard File’s PutFile dialog, we disable all files and enable
  280.     only directories.
  281.  
  282. *******************************************************************************/
  283. void FillList(ListHandle theList)
  284. {
  285.     short            vRefNum;
  286.     long            dirID;
  287.     short            index;
  288.     OSErr            err;
  289.     CInfoPBRec        pb;
  290.     ListItemHandle    listData;
  291.     Str255            itsName;
  292.     StringHandle    itsNameHandle;
  293.     Cell            cell = {0, 0};
  294.  
  295.     HGetVol(nil, &vRefNum, &dirID);
  296.     dirID = fsRtDirID;
  297.  
  298.     index = 0;
  299.     do {
  300.         ++index;
  301.         pb.hFileInfo.ioNamePtr = itsName;
  302.         pb.hFileInfo.ioVRefNum = vRefNum;
  303.         pb.hFileInfo.ioFDirIndex = index;
  304.         pb.hFileInfo.ioDirID = dirID;
  305.         err = PBGetCatInfoSync(&pb);
  306.  
  307.         if (err == noErr) {
  308.             listData = (ListItemHandle) NewHandle(sizeof(ListItemRecord));
  309.             (**listData).theString = NewString(itsName);
  310.             (**listData).iconID = kOurSICNs;
  311.             if (pb.hFileInfo.ioFlAttrib & ioDirMask) {    // we have a folder
  312.                 (**listData).iconIndex = 1;
  313.                 (**listData).enabled = TRUE;
  314.             } else {                                // check for app or doc
  315.                 if (pb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
  316.                     (**listData).iconIndex = 4;
  317.                 else
  318.                     (**listData).iconIndex = 0;
  319.                 (**listData).enabled = FALSE;
  320.             }
  321.             cell.v = LAddRow(1, 32767, theList);
  322.             LSetCell(&listData, sizeof(ListItemHandle), cell, theList);
  323.         }
  324.     } while (err == noErr);
  325. }
  326.  
  327.  
  328. /*******************************************************************************
  329.  
  330.     FlashDialogItem
  331.  
  332.     Utility to quickly flash a button in the dialog. We highlight the button
  333.     by calling HiliteControl, waiting a little bit so that the user can see
  334.     the button highlighted, and then turning off the button by calling
  335.     HiliteControl again.
  336.  
  337.     This routine assumes that the item we want to flash is backed up by a
  338.     Control Manager control, and really works best if the item is a simple
  339.     button control.
  340.  
  341. *******************************************************************************/
  342. void    FlashDialogItem(DialogPtr dlg, short itemToFlash)
  343. {
  344.     short    iKind;
  345.     Handle    iHandle;
  346.     Rect    iRect;
  347.     long    ignored;
  348.  
  349.     GetDItem(dlg, itemToFlash, &iKind, &iHandle, &iRect);
  350.     HiliteControl((ControlHandle) iHandle, 1);
  351.     Delay(8L, &ignored);
  352.     HiliteControl((ControlHandle) iHandle, 0);
  353. }
  354.  
  355.  
  356. /*******************************************************************************
  357.  
  358.     MyLDEF
  359.  
  360.     This is the entry point for the custom list definition. Normally, this
  361.     would be the entry point of an LDEF resource. However, we’re using a stub
  362.     LDEF resource that simply calls back into this routine.
  363.  
  364.     We handle three of the messages that can be sent to us: lInitMsg,
  365.     lDrawMsg, and lHiliteMsg. We don’t handle lCloseMsg because we don’t have
  366.     any special disposing needs.
  367.  
  368.     When called with the lInitMsg, we do three things. First, we calculate and
  369.     cache some values we’ll need for positioning our text and icons. We cache
  370.     these values in our ListRec in the “indent” field, which is ours to use.
  371.     Second, we set up our list’s refCon to point to our custom definition’s
  372.     entry point. This is so that our stub LDEF knows the address of the REAL
  373.     custom definition, and can call it for us in the future. Finally, we see
  374.     what drawing features are available to us. Normally, we do some good, old-
  375.     fashioned drawing by hand. However, if the icon utilities are available
  376.     to us and QuickDraw has grayish text mode implemented, we set a boolean
  377.     that we test later when we actually draw the text.
  378.  
  379.     For lDrawMsg and lHiliteMsg, we first need to find the data needed for the
  380.     cell we are told to handle. There is a field in the ListRec called “cells”
  381.     which contains a handle to all the data we’ve inserted into the list. This
  382.     data is the array of handles we inserted in FillList. We are also give an
  383.     offset into this array. Using these two pieces of information, we are able
  384.     to retrieve the correct handle.
  385.  
  386.     We defer drawing to a subroutine called DrawItem. However, we deal with
  387.     hiliting right here. If the item is enabled, we call InvertRect on it. If
  388.     the item is disabled, we leave it as it is to show the user that a
  389.     disabled item can’t be selected.
  390.  
  391. *******************************************************************************/
  392. pascal void MyLDEF(    short        lMessage,        // what operation to do
  393.                     Boolean        lSelect,        // draw it selected?
  394.                     Rect*        lRect,            // where to draw the item
  395.                     Cell        lCell,            // which cell to draw
  396.                     short        lDataOffset,    // offset to data for drawing
  397.                     short        lDataLen,        // length of that data
  398.                     ListHandle    lHandle)        // handle to list record
  399. {
  400.     FontInfo        info;
  401.     ListPtr            listPtr;
  402.     ListItemHandle    myDataHandle;
  403.     OSErr            err;
  404.     long            systemVersion;
  405.     long            qdFeatures;
  406.  
  407.     listPtr = *lHandle;
  408.     switch (lMessage) {
  409.         case lInitMsg:
  410.  
  411.             // Cache the vertical offset for the icon in indent.h
  412.             GetFontInfo(&info);
  413.             listPtr->indent.h = (listPtr->cellSize.v - 16) / 2;
  414.  
  415.             // Cache the vertical offset for the text in indent.v
  416.             listPtr->indent.v = (listPtr->cellSize.v - (info.ascent + info.descent)) / 2
  417.                               + info.ascent;
  418.  
  419.             // Point refCon to this entry point so that stub LDEF knows where to go.
  420.             listPtr->refCon = (long) MyLDEF;
  421.  
  422.             // See what kinds of drawing facilities are available to us. In order
  423.             // to use the icon drawing routines, check that we are running under
  424.             // System 7 or later. To see if we have grayishText mode, make the
  425.             // appropriate Gestalt call.
  426.             listPtr->userHandle = (Handle) FALSE;
  427.             if ((Gestalt(gestaltSystemVersion, &systemVersion) == noErr)
  428.                 && (systemVersion >= 0x0700)
  429.                 && (Gestalt(gestaltQuickdrawFeatures, &qdFeatures) == noErr)
  430.                 && (qdFeatures & (1 << gestaltHasGrayishTextOr)))
  431.                     listPtr->userHandle = (Handle) TRUE;
  432.  
  433.             break;
  434.  
  435.         case lDrawMsg:
  436.  
  437.             myDataHandle = *(ListItemHandle *)((*listPtr->cells) + lDataOffset);
  438.             if (myDataHandle != nil)
  439.                 DrawItem(lSelect, lRect, myDataHandle, lHandle);
  440.  
  441.             break;
  442.  
  443.         case lHiliteMsg:
  444.  
  445.             myDataHandle = *(ListItemHandle *)((*listPtr->cells) + lDataOffset);
  446.             if ((myDataHandle != nil) && ((**myDataHandle).enabled))
  447.                 InvertRect(lRect);
  448.  
  449.             break;
  450.     }
  451. }
  452.  
  453.  
  454. /*******************************************************************************
  455.  
  456.     DrawItem
  457.  
  458.     This is the routine used to draw each item in the list. We draw items in
  459.     one of two ways, depending on the capabilities of our machine. If we have
  460.     the necessary drawing routines (as determined when our LDEF received the
  461.     lInitMsg), we use those drawing routines to draw the text and icon of
  462.     disabled items in real gray or dithered gray. If these drawing features
  463.     aren’t available, then we draw in dithered black and white by hand.
  464.  
  465. *******************************************************************************/
  466. void DrawItem(Boolean selected, Rect* bounds, ListItemHandle data, ListHandle lHandle)
  467. {
  468.     PenState        ps;
  469.     short            oldTextMode;
  470.     Boolean            doNewWay;
  471.  
  472.     GetPenState(&ps);
  473.     oldTextMode = qd.thePort->txMode;
  474.  
  475.     doNewWay = (Boolean) (**lHandle).userHandle;
  476.  
  477.     if (doNewWay) {
  478.         DrawNewWay(selected, bounds, data, lHandle);
  479.     } else {
  480.         DrawOldWay(selected, bounds, data, lHandle);
  481.     }
  482.  
  483.     TextMode(oldTextMode);
  484.     SetPenState(&ps);
  485. }
  486.  
  487.  
  488. /*******************************************************************************
  489.  
  490.     DrawNewWay
  491.  
  492.     Called to draw our of list items when we have grayishTextOr mode and the
  493.     icon utilities available to us. grayishTextOr mode will draw the text
  494.     either in true gray if the monitor can support it, or in dithered gray
  495.     otherwise.
  496.     
  497.     We use the icon utilities to draw the icon in the same way. PlotIconID
  498.     is a newly documented routine that draws icons the same way the Finder
  499.     does. It takes four parameters: the rectangle the icon should be drawn
  500.     in, the alignment that should be used when positioning the icon, the
  501.     transformation that should be applied to the raw icon (in other words,
  502.     whether the icon should be drawn as “selected,” “open,” etc.), and the
  503.     resource ID of the raw icon itself. We get this resource ID from the
  504.     iconIndex field of our list entry record. When drawing the “old” way,
  505.     we use this field as an index into a 'SICN' resource, which is an array
  506.     of small icons. However, when drawing the “new” way, as we are here, we
  507.     deal with an array of resources, so we use iconIndex as the index into
  508.     this new array. Because application resources must be numbered in the
  509.     128...32767 range, and since iconIndex normally starts at 0, we must
  510.     add 128 to iconIndex to get it into the right range.
  511.     
  512. *******************************************************************************/
  513. void DrawNewWay(Boolean selected, Rect* bounds, ListItemHandle data,
  514.                 ListHandle lHandle)
  515. {
  516.     IconTransformType    iconTransform;
  517.     Boolean                itemEnabled = (**data).enabled;
  518.     Rect                iconRect;
  519.  
  520.     iconTransform = (itemEnabled ? ttNone : ttDisabled);
  521.  
  522.     iconRect.left = bounds->left;
  523.     iconRect.top = bounds->top + (**lHandle).indent.h;
  524.     iconRect.bottom = iconRect.top + 16;
  525.     iconRect.right = iconRect.left + 16;
  526.  
  527.     PlotIconID(&iconRect, atNone, iconTransform, (**data).iconIndex + 128);
  528.  
  529.     TextMode(itemEnabled ? srcOr : grayishTextOr);
  530.     DrawTheText(bounds, lHandle, data);
  531.  
  532.     if (selected && itemEnabled)
  533.         InvertRect(bounds);
  534. }
  535.  
  536.  
  537. /*******************************************************************************
  538.  
  539.     DrawOldWay
  540.  
  541.     This routine is called if we aren’t running under 7.0. Here, we simply
  542.     draw the text and the icon in black. If the item should be disabled, we
  543.     gray it out by calling our GrayOut utility.
  544.  
  545. *******************************************************************************/
  546. void DrawOldWay(Boolean selected, Rect* bounds, ListItemHandle data,
  547.                 ListHandle lHandle)
  548. {
  549.     DrawTheIcon(bounds, lHandle, data);
  550.     DrawTheText(bounds, lHandle, data);
  551.  
  552.     if (!(**data).enabled) {
  553.         GrayOut(bounds);
  554.     }
  555.  
  556.     if (selected && (**data).enabled)
  557.         InvertRect(bounds);
  558. }
  559.  
  560.  
  561. /*******************************************************************************
  562.  
  563.     DrawTheIcon
  564.  
  565.     This is the routine used by DrawNewWay and DrawOldWay to prepare the
  566.     drawing of the icon. We do the actually drawing with a call to another
  567.     utility routine called DrawSmallIcon. However, we first need to set up a
  568.     rectangle for DrawSmallIcon to draw in.
  569.  
  570. *******************************************************************************/
  571. void DrawTheIcon(Rect *bounds, ListHandle lHandle, ListItemHandle data)
  572. {
  573.     Rect            iconRect;
  574.  
  575.     iconRect = *bounds;
  576.     iconRect.top += (**lHandle).indent.h;
  577.     iconRect.bottom = iconRect.top + 16;
  578.     iconRect.right = iconRect.left + 16;
  579.     DrawSmallIcon( (**data).iconID, (**data).iconIndex, &iconRect);
  580. }
  581.  
  582.  
  583. /*******************************************************************************
  584.  
  585.     DrawTheText
  586.  
  587.     This is the routine used by DrawNewWay and DrawOldWay to draw the actual
  588.     text. Nothing special here. We indent from the left 20 pixels, and we move
  589.     down vertically by the amount we calculated in our initialization routine.
  590.     Then we call DrawString to draw the text.
  591.  
  592. *******************************************************************************/
  593. void DrawTheText(Rect *bounds, ListHandle lHandle, ListItemHandle data)
  594. {
  595.     StringHandle    theString;
  596.  
  597.     MoveTo(bounds->left + 20, bounds->top + (**lHandle).indent.v);
  598.     theString = (**data).theString;
  599.     HLock((Handle) theString);
  600.     DrawString(*theString);
  601.     HUnlock((Handle) theString);
  602. }
  603.  
  604.  
  605. /*******************************************************************************
  606.  
  607.     GrayOut
  608.  
  609.     Small routine to take an area and make it appear grayed out. This is done
  610.     by taking QuickDraw’s global gray pattern and using it to erase every
  611.     other bit in the specified rectangle. This erasing is done using
  612.     QuickDraw’s srcBic -- or Source Bit Clear -- mode. Source Bit Clear means
  613.     that each black bit in the source should result in the erasure of the
  614.     corresponding bit in the destination.
  615.  
  616. *******************************************************************************/
  617. void GrayOut(Rect* bounds)
  618. {
  619.     PenNormal();
  620.     PenPat(qd.gray);
  621.     PenMode(srcBic);
  622.     PaintRect(bounds);
  623. }
  624.  
  625.  
  626. /*******************************************************************************
  627.  
  628.     DrawSmallIcon
  629.  
  630.     Short utility that draws a small icon. We pass in the resource ID of a
  631.     SICN. Since this resource contains a series of indexed icons, we also pass
  632.     in which icon to draw. Finally, we specify where we want the icon drawn.
  633.     Since this location is specified as a rectangle, we can actually scale the
  634.     icon if we want by specifying a rectangle that is not 16x16.
  635.  
  636.     Drawing the small icon is simply a matter of calling CopyBits. We fill out
  637.     the fields of a bitmap, and then get the resource. Each icon is 32 bits
  638.     long, so we point to 32*index bytes into the resource to get the icon we
  639.     want. This gives us the base address for our icon’s bitmap. Once that
  640.     field is filled out, we call CopyBits.
  641.  
  642. *******************************************************************************/
  643. void DrawSmallIcon(short id, short index, Rect* where)
  644. {
  645.     Handle        sicnData;
  646.     BitMap        bm = {    nil,                // baseAddr
  647.                         2,                    // rowBytes
  648.                         {0, 0, 16, 16}};    // bounds
  649.  
  650.     sicnData = GetResource('SICN', id);
  651.     if (sicnData != nil) {
  652.         bm.baseAddr = (*sicnData) + (index*32);
  653.         CopyBits(&bm, &qd.thePort->portBits, &bm.bounds, where, srcCopy, nil);
  654.         ReleaseResource(sicnData);
  655.     }
  656. }
  657.  
  658.